<?php

namespace common\service;

use common\exception\LogicException;
use common\model\EntityModel;
use common\model\ProjectModel;
use common\model\ReviewRequestModel;
use common\model\ReviewRequestNodeModel;
use common\model\TaskMemberModel;
use common\model\TaskModel;
use common\model\TaskRelationTagModel;
use common\model\TaskWorkOrderModel;
use support\EntityTaskFilterParser;
use support\ErrorCode;
use support\SingletonTrait;
use think\Exception;
use Throwable;


class TaskLibraryV2Service
{
    use SingletonTrait;
    use EntityTaskFilterParser;

    /**
     * @var string[]
     */
    protected $listQueryTaskField = [
        "module.code as module_code",
        'entity.name',
        'entity.code',
        'entity.code as entity_code',
        'entity.initial_episode_code',
        'entity.showings_number',
        'entity.entity_id as entity_parent_id',
        'entity.step_workflow_id',
        'entity.module_id as entity_module_id',
        'entity.json as entity_json',
        'entity.director_star as director_star',
        'entity.key_shot as key_shot',
        'entity.is_append as is_append',
        "entity.step_workflow_id",
        'task.id',
        'task.entity_id',
        'task.name as task_name',
        'task.plan_start_time',
        'task.plan_end_time',
        'task.plan_delivery_time',
        'task.end_time',
        'task.step_id',
        'task.step_type',
        'task.step_category_id',
        'task.plan_start_time',
        'task.is_assignee',
        'task.assignee',
        'task.executor',
        'task.assignee_producer',
        'task.tenant_id',
        'task.executor_tenant_id',
        'task.department_id',
        'task.json as task_json',
        "task.project_id",
        "task.executor_type",
        "task.assign_status",
        "task.created",
        "task.created_by",
        "task.updated",
        "task.manual_man_hour",
        "task.actual_man_hour",
        "task.delivery_time",
        'task.episode_id as episode_id',
        "JSON_UNQUOTE(JSON_EXTRACT(entity.`json`, '$.storyboard_is_cluster')) AS storyboard_is_cluster",
        "JSON_UNQUOTE(JSON_EXTRACT(entity.`json`, '$.shot_is_cluster')) AS shot_is_cluster",
        "JSON_UNQUOTE(JSON_EXTRACT(task.`json`, '$.task_status')) AS task_status_id",
        "JSON_UNQUOTE(JSON_EXTRACT(task.`json`, '$.is_dismantle_task')) AS is_dismantle_task",
        "JSON_UNQUOTE(JSON_EXTRACT(task.`json`, '$.task_workload_remain')) AS task_workload_remain",
    ];

    /**
     * @var array|array[]
     */
    public $moduleIdMap = [];
    /**
     * @var object|\think\Model|\think\model\RelationModel
     */
    private $taskModel;

    public function isStandardProduction()
    {
        return true;
    }

    public function __construct()
    {
        $this->taskModel = model(TaskModel::class);
        $this->moduleIdMap = [
            'asset' => [
                module_by_code('design')['id'],
                module_by_code('asset')['id'],
                module_by_code('level')['id'],
            ],
            'shot' => [
                module_by_code('shot')['id'],
                module_by_code('storyboard')['id'],
            ]
        ];
    }


    /**
     * 任务库列表查询
     * @param $data
     * @param int $userId
     * @param int $tenantId
     * @return array
     */
    public function list($data, int $userId, int $tenantId)
    {
        $param = $data['param'] ?? [];
        $type = $data['type'] ?? "step";
        $fields = $param['fields'] ?? [];
        $filter = $param['filter'] ?? [];
        $page = $param['page'] ?? [1, C('default_page_total')];
        $showPage = $param['show_page'] ?? 1;// 是否显示分页--默认显示

        //传了任务ID  分页
        if (empty($param['page']) && !empty($filter['task.id'][1])) {
            $page = [1, count(explode(',', $filter['task.id'][1]))];
        }
        //不显示分页 --- 任务库所有有可能超过1000
        if (!$showPage) {
            $page = [1, C("database.database_max_select_rows") * 10];
        }

        $projectId = $param['filter']['project_id'] ?? null;
        $initialEpisodeCodeList = $param['filter']['episode_list'] ?? null;
        $res = ['rows' => [], 'total' => 0];
        $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();
        unset($filter['is_filter_overdue'], $filter['sequence_name'], $filter['group_todo'], $filter['abnormal'], $filter['episode_list']);
        $filter = $this->parseMultipleEntityTaskFilter($filter);
        $filter['task.step_type'] = $type;
        $filter["JSON_UNQUOTE(JSON_EXTRACT(entity.json, concat('$.', module.code,'_status')))"] = ['NEQ', $closedStatus['id']];
        $filter[] = $this->appendFilter($param['filter'], $data['module_code'], $userId, $tenantId, $projectId);
        if (!empty($initialEpisodeCodeList)) {
            $sql = EpisodeAllocationService::getInstance()->getEpisodeShowingsSql($initialEpisodeCodeList, 'initial_episode_code', 'showings_number');
            $filter[] = ['_string' => $sql];
        }
        if ($page[0] == 1 && $showPage) {
            $total = ProducerManageService::getInstance()->getTaskListQuery($this->tableList, ['entity', 'module', 'step'])
                ->join('entity on entity.id = task.entity_id')
                ->join('module on entity.module_id = module.id')
                ->join('step on step.id = task.step_id')
                ->where($filter)
                ->cache(60)
                ->count();
            if ($total == 0) {
                return $res;
            }
            $res['total'] = $total;
        }
        $fields = explode(',', $fields);
        $taskList = ProducerManageService::getInstance()->getTaskListQuery($this->tableList, ['entity', 'module', 'step'])
            ->join('entity on entity.id = task.entity_id')
            ->join('module on entity.module_id = module.id')
            ->join('step on step.id = task.step_id')
            ->where($filter)
            ->order($param['order'] ?? 'task.order_str')
            ->field($fields)
            ->page(...$page)
            ->select();
        if (empty($taskList)) {
            return $res;
        }
        $res['rows'] = $taskList;
        return $res;
    }

    /**
     * 返回数据处理
     * @param $tenantId
     * @param $taskList
     * @return array
     */
    public function returnData($tenantId, $taskList)
    {
        return array_map(function ($taskItem) use ($tenantId) {
            $complexData = separate_complex_query_data($taskItem, '__');
            $complexData['task']['is_dismantle_task'] = (bool)$complexData['task']['is_dismantle_task'];
            if (!empty($complexData['module']['code'])) {
                $complexData['task']['module_code'] = in_array($complexData['module']['code'], ['design', 'asset', 'level', '']) ? 'asset' : 'shot';
            }
            if (!empty($complexData['task']['executor_tenant_id'])) {
                $complexData['task']['is_outside'] = false;  // 是否外包
                if ($complexData['task']['executor_tenant_id'] > 0 && $complexData['task']['executor_tenant_id'] != $tenantId) {
                    $complexData['task']['is_outside'] = true;
                }
            }
            // 工作量剩余
            $complexData['task']['task_workload_remain'] = !is_null($complexData['task']['task_workload_remain']) ? intval($complexData['task']['task_workload_remain']) : 100;

            $entityJsonArray = json_decode($complexData['entity']['json'], true);
//            $taskJson = json_decode($complexData['task']['json']);
            $complexData['entity']['is_cluster'] = 'no';
            $fixIsClusterKey = $complexData['module']['code'] . '_' . 'is_cluster';
            if (!empty($entityJsonArray[$fixIsClusterKey]) && $entityJsonArray[$fixIsClusterKey] == 'yes') {
                $complexData['entity']['is_cluster'] = 'yes';
            }
            unset($complexData['entity']['json'], $complexData['task']['json']);
            return $complexData;
        }, $taskList);
    }


    /**
     * 分组列表
     * @param $data
     * @param int $tenantId
     * @param int $userId
     * @return array
     */
    public function groupList($data, int $tenantId, int $userId)
    {
        $param = $data['param'] ?? [];
        $fields = empty($param['fields']) ? [] : explode(',', $param['fields']);
        $filter = $param['filter'];
        $page = $param['page'] ?? [1, C('default_page_total')];
        $projectId = $param['filter']['task.project_id'] ?? null;
        $initialEpisodeCodeList = $param['filter']['episode_list'] ?? null;
        $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();

        unset($filter['is_filter_overdue'], $filter['sequence_name'], $filter['group_todo'], $filter['abnormal'], $filter['episode_list']);
        $filter = $this->parseMultipleEntityTaskFilter($filter);
        $filter['task.step_type'] = $data['type'] ?? "step";
        $filter["JSON_UNQUOTE(JSON_EXTRACT(entity.json, concat('$.', module.code,'_status')))"] = ['NEQ', $closedStatus['id']];

        $filter[] = $this->appendFilter($param['filter'], $data['module_code'], $userId, $tenantId, $projectId);
        if (!empty($initialEpisodeCodeList)) {
            $sql = EpisodeAllocationService::getInstance()->getEpisodeShowingsSql($initialEpisodeCodeList, 'initial_episode_code', 'showings_number');
            $filter[] = ['_string' => $sql];
        }
        $initFields = [
            'entity.name as entity_name',
            "task.name as task_name",
            "count(task.id) as task_num",
            "JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.', module.code, '_status'))) as entity_status",
            "JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.', module.code, '_category'))) as category",
            "JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.', module.code, '_grade'))) as grade"
        ];

        $res = [];
        if ($page[0] == 1) {
            $countModuleId = module_by_code('design')['id'];
            if ($data['module_code'] == 'asset') {
                $countModuleId = module_by_code('design')['id'];
            }
            if ($data['module_code'] == 'shot') {
                $countModuleId = module_by_code('storyboard')['id'];
            }
            $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();
            $count = model(EntityModel::class)
                ->join('module on module.id = entity.module_id')
                ->where([
                    "JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.', module.code, '_status')))" => ['NEQ', $closedStatus['id']]
                ])
                ->where(['entity.module_id' => $countModuleId, 'entity.project_id' => $projectId])
                ->count();
            if (empty($count)) {
                return [
                    'total' => 0,
                    'rows' => []
                ];
            }
            $res['total'] = $count;
        }

        $fields = array_merge($fields, $initFields);
        $taskList = ProducerManageService::getInstance()->getTaskListQuery($this->tableList, ['entity', 'module', 'step'])
            ->join('entity on entity.id = task.entity_id')
            ->join('module on entity.module_id = module.id')
            ->join('step on step.id = task.step_id')
            ->join('step_category on step_category.id = task.step_category_id')
            ->where($filter)
            ->order($param['order'] ?? 'task.order_str')
            ->field($fields)
            ->page($page[0], $page[1])
            ->group($param['group'] ?? 'task.order_str')
            ->cache(5)
            ->select();
        $res['rows'] = $taskList;
        return $res;
    }

    /**
     * 附加过滤条件
     * @param $param
     * @param $moduleCode
     * @param $userId
     * @param $tenantId
     * @param $projectId
     * @return array
     */
    public function appendFilter($param, $moduleCode, $userId, $tenantId, $projectId)
    {
        $moduleIds = [];
        if (!empty($this->moduleIdMap[$moduleCode])) {
            $moduleIds = $this->moduleIdMap[$moduleCode];
        }
        $filter = [];
        if (!empty($moduleIds)) {
            $filter['task.entity_module_id'] = ['IN', join(',', $moduleIds)];
        }
        if (!empty($param['is_me_executor'])) { // 仅我执行
            $filter['task.executor'] = $userId;
        }
        if (!empty($param['is_me_review'])) { // 仅我审核
            $versionData = VersionService::getInstance()->getMeExecutorReviewInfo($userId, $tenantId, [$projectId], $filter);
            if (empty($versionData)) {
                $filter[]['task.id'] = 0;
                return $filter;
            }
            $filter[]['task.id'] = ['IN', array_column($versionData, 'link_id')];
        }
        if (!empty($param['task_work_order_filter_is_abnormal'])) { // 是否异常
            $param['filter']['task.project_id'] = $projectId;
            $abnormalId = TaskLibraryService::getInstance()->queryAbnormalTaskIdList($param['filter']);
            if (empty($abnormalId)) {
                $filter[]['task.id'] = 0;
                return $filter;
            }
            $filter[]['task.id'] = ['IN', $abnormalId];
        }
//        if (!empty($param['category'])) {
//            $filter["JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.', module.code, '_category')))"] = ['IN', $param['category']];
//        }
//        if (!empty($param['grade'])) {
//            $filter["JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.', module.code, '_grade')))"] = ['IN', $param['grade']];
//        }
        return $filter;
    }

    /**
     * 异常情况
     * @param $taskIds
     * @return array|mixed|string
     */
    public function abnormalDetail($taskIds)
    {
        if (empty($taskIds)) {
            return [];
        }
        $field = [
            'id',
            'plan_start_time',
            'plan_end_time',
            'end_time',
            'assign_status',
            'step_type',
            "JSON_UNQUOTE(JSON_EXTRACT(json, '$.task_status')) as task_status"
        ];
        $taskList = model(TaskModel::class)
            ->where(['task.id' => ['IN', $taskIds]])
            ->field($field)
            ->select();
        if (empty($taskList)) {
            return [];
        }
        $taskWorkOrderListStatusCheckList = model(TaskWorkOrderModel::class)
            ->where(['task_id' => ['IN', $taskIds], 'status' => ['IN', TaskWorkOrderModel::STATUS_NOT_CLOSED_GROUP]])
            ->field("task_id,concat(task_id,'#',status) ck")
            ->group('task_id,status')
            ->select();
        $notClosedWorkOrderTaskIdList = array_column($taskWorkOrderListStatusCheckList, 'task_id', 'task_id');
        $taskWorkOrderListStatusCheckList = array_column($taskWorkOrderListStatusCheckList, 'ck', 'ck');

        $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();
        $doneStatus = StatusService::getInstance()->getTaskDoneStatus();
        $notStartedStatus = StatusService::getInstance()->getNotStartedStatus();
        $notStartedStatus = array_column($notStartedStatus, 'id');

        foreach ($taskList as &$taskItem) {
            $currentTime = time();
            if ($taskItem['step_type'] == TaskModel::STEP_TYPE_STEP) {
                $taskItem['has_unclose_work_order'] = $notClosedWorkOrderTaskIdList[$taskItem['id']] ?? 0;
                $taskItem['not_assigned'] = false;
                $taskItem['not_started'] = false;
                $taskItem['work_order_not_handled'] = false;
                if ($taskItem['plan_start_time'] > 0 && $taskItem['plan_start_time'] < $currentTime && $taskItem['assign_status'] == TaskModel::ASSIGN_STATUS_DEFAULT) {
                    $taskItem['not_assigned'] = true;
                }
                if ($taskItem['plan_start_time'] > 0 && $taskItem['plan_start_time'] < $currentTime && in_array($taskItem['task_status'], $notStartedStatus)) {
                    $taskItem['not_started'] = true;
                }
                if (isset($taskWorkOrderListStatusCheckList[$taskItem['id'] . '#' . TaskWorkOrderModel::STATUS_UNTREATED])) {
                    $taskItem['work_order_not_handled'] = true;
                }
            }
            if (!in_array($taskItem['task_status'], [$closedStatus['id'], $doneStatus['id']]) && $taskItem['plan_end_time'] < $currentTime && $taskItem['plan_end_time'] > 0) {
                $taskItem['overdue'] = time() - $taskItem['plan_end_time'];
            }
        }
        return $taskList;
    }

    /**
     * 审核进度
     * @param $taskIds
     * @param $tenantId
     * @return array|mixed|string
     * @throws \Exception
     */
    public function reviewProgress($taskIds, $tenantId)
    {
        if (empty($taskIds)) {
            return [];
        }
        $field = [
            'id',
            'plan_start_time',
            'plan_end_time',
            'end_time',
            'assign_status',
            'step_type',
            "JSON_UNQUOTE(JSON_EXTRACT(json, '$.task_status')) as task_status",
            'executor_tenant_id',
            'tenant_id'
        ];
        $taskList = model(TaskModel::class)
            ->where(['task.id' => ['IN', $taskIds]])
            ->field($field)
            ->select();
        if (empty($taskList)) {
            return [];
        }
        $insideTaskList = [];
        $outsideTaskId = [];
        foreach ($taskList as $taskItem) {
            if ($taskItem['executor_tenant_id'] > 0 && $taskItem['executor_tenant_id'] != $taskItem['tenant_id']) {
                $outsideTaskId[] = $taskItem['id'];
            } else {
                $insideTaskList[] = $taskItem['id'];
            }
        }

        //获取外包任务映射id
        $outsideMap = ReviewTaskService::getInstance()->getOutsideMap($insideTaskList, $outsideTaskId);
        $insideTaskIdList = $outsideMap['task_ids'];
        $outsideTaskIdMap = $outsideMap['b_to_a_map'];
        // 审核版本map
        $reviewDataMap = [];
        //版本查询
        $maxVersion = VersionService::getInstance()->getTaskMaxVersionInfo($insideTaskIdList, "id,number,link_id,param,is_publish,is_pass,description");
        // 审核进度
        $reviewProgressData = [];
        $versionReviewRequestMap = [];
        $fields = [
            "review_request.id",
            "review_request.name",
            "review_request.created",
            "review_request_node.id",
            "review_request_node.is_pass",
            "review_request_node.is_reviewed",
            "review_request_node.reviewed",
            "review_request_node.start_at",
            "review_request_node.plan_reviewer",
            "review_request.link_id",
        ];
        if (!empty($maxVersion)) {
            $versionModuleData = module_by_code('version');
            $reviewData = model(ReviewRequestModel::class)
                ->join("review_request_node on review_request.id = review_request_node.review_request_id")
                ->where(['review_request.link_id' => ['IN', array_column($maxVersion, 'id')]])
                ->where(['review_request.module_id' => $versionModuleData['id']])
                ->where(['review_request_node.is_active' => 'yes', 'review_request.tenant_id' => $tenantId])
                ->field(build_complex_query_fields($fields, '__'))
                ->select();
            $reviewRequestList = model(ReviewRequestModel::class)
                ->where(['module_id' => $versionModuleData['id'], 'link_id' => ['IN', array_column($maxVersion, 'id')]])
                ->select();
            $versionReviewRequestMap = array_column($reviewRequestList, 'id', 'link_id');
            $reviewRequestIdList = array_column($reviewRequestList, 'id');
            foreach ($reviewData as &$item) {
                $item = separate_complex_query_data($item, '__');
                $item['link_id'] = $item['review_request']['link_id'];
            }
            $reviewDataMap = array_column($reviewData, null, 'link_id');
            $reviewProgressData = ReviewRequestService::getInstance()->queryReviewRequestProgressData($reviewRequestIdList);
        }
        $version = [];
        foreach ($maxVersion as $maxVersionItem) {
            if (isset($outsideTaskIdMap[$maxVersionItem['link_id']])) {
                $version[$outsideTaskIdMap[$maxVersionItem['link_id']]] = $maxVersionItem;
            } else {
                $version[$maxVersionItem['link_id']] = $maxVersionItem;
            }
        }
        foreach ($taskList as &$taskItem) {
            $taskItem['version'] = $version[$taskItem['id']] ?? null;
            $linkId = $taskItem['version']['id'] ?? '';
            // 审核信息
            $taskItem['review_request'] = $reviewDataMap[$linkId]['review_request'] ?? null;
            // 审核节点信息
            $taskItem['review_request_node'] = $reviewDataMap[$linkId]['review_request_node'] ?? null;
            // 审核进度
            $taskItem['review_progress'] = null;
            $reviewRequestId = $versionReviewRequestMap[$linkId] ?? 0;
            if ($reviewRequestId > 0) {
                $taskItem['review_progress'] = $reviewProgressData[$reviewRequestId] ?? null;
            }
        }
        return $taskList;
    }

    /**
     * 查看任务延期情况
     * @param $taskIds
     * @return array
     */
    public function taskDelayInfo($taskIds)
    {
        return ReviewTaskDelayService::getInstance()->taskDelayInfo($taskIds);
    }

    /**
     * 排期时间查询
     * @param $episodeIds
     * @param $stepCategoryIds
     * @return array
     */
    public function getEpisodeStepCategoryPlanList($episodeIds, $stepCategoryIds)
    {
        return PlanService::getInstance()->getEpisodeStepCategoryPlanObject($episodeIds, $stepCategoryIds);
    }

    /**
     * 批量修改预估工时
     * @param $updData
     * @return array|void
     * @throws \Exception
     */
    public function bathUpdateManualManHour($updData)
    {
        if (empty($updData)) {
            return [];
        }

        $errMessage = $this->bathUpdateManualManHourValidate($updData);
        if (!empty($errMessage)) {
            return $errMessage;
        }
        try {
            CommonService::instance('task')->commonBatchUpdate($updData);
        } catch (Exception $e) {
            throw new LogicException($e->getMessage(), ErrorCode::UPDATE_TASK_FAIL);
        }
    }

    /**
     * 批量修改预估工时校验
     * @param $updData
     * @return array
     */
    public function bathUpdateManualManHourValidate($updData)
    {
        $taskIds = array_column($updData, 'id');
        $taskField = [
            'id',
            'task.step_type',
            "JSON_UNQUOTE(JSON_EXTRACT(task.json, '$.task_status')) as task_status_id"
        ];
        $closedStatusId = StatusService::getInstance()->getTaskDefaultClosedStatus()['id'];
        $finalStatusId = StatusService::getInstance()->getTaskDoneStatus()['id'];
        $taskList = model(TaskModel::class)
            ->field($taskField)
            ->where(['id' => ['IN', $taskIds]])
            ->select();
        $taskMap = [];
        if (!empty($taskList)) {
            $taskMap = array_column($taskList, null, 'id');
        }
        // 查询任务执行人列表
        $taskExecutorList = model(TaskMemberModel::class)
            ->field('task_id,partner')
            ->where(['task_id' => ['IN', $taskIds]])
            ->select();
        $taskExecutorMap = [];
        if (!empty($taskExecutorList)) {
            $taskExecutorMap = array_column($taskExecutorList, null, 'task_id');
        }
        // 修改校验
        $res = [];
        foreach ($updData as $item) {
            if (!empty($taskMap[$item['id']]) && $taskMap[$item['id']]['task_status_id'] == $finalStatusId) {
                $res[$item['id']] = [
                    'code' => 0,
                    'msg' => "任务已完成,不能修改预估工时"
                ];
            }
            if (!empty($taskMap[$item['id']]) && $taskMap[$item['id']]['task_status_id'] == $closedStatusId) {
                $res[$item['id']] = [
                    'code' => 0,
                    'msg' => "任务已取消,不能修改预估工时"
                ];
            }
            if (!empty($taskMap[$item['id']]) && $taskMap[$item['id']]['step_type'] != TaskModel::STEP_TYPE_STEP) {
                $res[$item['id']] = [
                    'code' => 0,
                    'msg' => "只能修改工序任务预估工时"
                ];
            }
            if (!empty($taskExecutorMap[$item['id']])) {
                $res[$item['id']] = [
                    'code' => 0,
                    'msg' => "只有执行人才能修改预估工时"
                ];
            }

        }
        return $res;
    }

    /**
     * 获取任务详情列表json转数组
     * @param $param
     * @return array
     */
    public function getTaskInfoList($param)
    {
        $taskList = model(TaskModel::class)->where($param['filter'])->select();
        return array_map(function ($tmp) use ($param) {
            $returnData = [];
            $tmp = array_merge($tmp, json_decode($tmp['json'], true));
            unset($tmp['json']);
            foreach ($param['fields'] as $field) {
                $returnData[$field] = $tmp[$field] ?? null;
            }
            return $returnData;
        }, $taskList);
    }

    /**
     * 获取实体详情列表json转数组
     * @param $param
     * @return array
     */
    public function getEntityInfoList($param)
    {
        $taskList = model(EntityModel::class)->where($param['filter'])->select();
        return array_map(function ($tmp) use ($param) {
            $returnData = [];
            $tmp = array_merge($tmp, json_decode($tmp['json'], true));
            unset($tmp['json']);
            foreach ($param['fields'] as $field) {
                $returnData[$field] = $tmp[$field] ?? null;
            }
            return $returnData;
        }, $taskList);
    }


    /**
     * 审核中
     * @param $taskIds
     * @return array
     */
    public function reviewIng($taskIds)
    {
        if (empty($taskIds)) {
            return [];
        }
        $field = [
            'id',
            'plan_start_time',
            'plan_end_time',
            'end_time',
            'assign_status',
            'step_type',
            "JSON_UNQUOTE(JSON_EXTRACT(json, '$.task_status')) as task_status",
            'executor_tenant_id',
            'tenant_id'
        ];
        $taskList = model(TaskModel::class)
            ->where(['task.id' => ['IN', $taskIds]])
            ->field($field)
            ->select();
        if (empty($taskList)) {
            return [];
        }
        $insideTaskList = [];
        $outsideTaskId = [];
        foreach ($taskList as $taskItem) {
            if ($taskItem['executor_tenant_id'] > 0 && $taskItem['executor_tenant_id'] != $taskItem['tenant_id']) {
                $outsideTaskId[] = $taskItem['id'];
            } else {
                $insideTaskList[] = $taskItem['id'];
            }
        }
        //获取外包任务映射id
        $outsideMap = ReviewTaskService::getInstance()->getOutsideMap($insideTaskList, $outsideTaskId);
        $insideTaskIdList = $outsideMap['task_ids'];
        //版本查询
        $maxVersion = VersionService::getInstance()->getTaskMaxVersionInfo($insideTaskIdList, "id,number,link_id,param,is_publish,is_pass,description");
        $versionReviewRequestMap = [];
        $reviewIngMap = [];
        if (!empty($maxVersion)) {
            $versionModuleData = module_by_code('version');
            $reviewRequestList = model(ReviewRequestModel::class)
                ->where(['module_id' => $versionModuleData['id'], 'link_id' => ['IN', array_column($maxVersion, 'id')]])
                ->select();
            $versionReviewRequestMap = array_column($reviewRequestList, 'id', 'link_id');
            $reviewRequestIdList = array_column($reviewRequestList, 'id');
            $reviewIngData = model(ReviewRequestNodeModel::class)
                ->where(['review_request_id' => ['IN', implode(',', $reviewRequestIdList)]])
                ->where(['is_active' => 'yes', 'is_reviewed' => 'no', 'is_pass' => 'no'])
                ->order('parent_id ASC')->select();
            $reviewIngMap = array_group_by($reviewIngData, 'review_request_id');
        }
        $version = [];
        foreach ($maxVersion as $maxVersionItem) {
            if (isset($outsideTaskIdMap[$maxVersionItem['link_id']])) {
                $version[$outsideTaskIdMap[$maxVersionItem['link_id']]] = $maxVersionItem;
            } else {
                $version[$maxVersionItem['link_id']] = $maxVersionItem;
            }
        }
        $returnData = [];
        foreach ($taskList as $taskItem) {
            // 审核中
            $linkId = $version[$taskItem['id']]['id'] ?? 0;
            if ($linkId > 0) {
                $reviewRequestId = $versionReviewRequestMap[$linkId] ?? 0;
                if ($reviewRequestId > 0 && !empty($reviewIngMap[$reviewRequestId][0]['plan_reviewer'])) {
                    $returnData[] = [
                        'id' => $taskItem['id'],
                        'plan_reviewer' => $reviewIngMap[$reviewRequestId][0]['plan_reviewer'],
                        'review_request_id' => $reviewRequestId,
                    ];
                }
            }
        }
        return $returnData;
    }

    /**
     * 审核进度详情
     * @param $reviewRequestId
     * @return array|false|mixed|string
     * @throws \Exception
     */
    public function reviewRequestProgressList($reviewRequestId)
    {
        if (empty($reviewRequestId)) {
            return [];
        }
        return ReviewRequestService::getInstance()->queryReviewRequestProgressList($reviewRequestId);
    }

    /**
     * 批量修改制作时间
     * @param $data
     * @return array
     * @throws \Exception
     */
    public function bathUpdateTaskScheduleDate($data)
    {
        if (empty($data['data_list'])) {
            return [];
        }
        $errorList = [];
        $successList = [];
        $taskIds = array_column($data['data_list'], 'id');
        $taskList = $this->taskModel->field('id,plan_end_time,plan_start_time')->where(['id' => ['IN', $taskIds]])->select();
        if (empty($taskList)) {
            return [];
        }
        $taskMap = array_column($taskList, null, 'id');
        $delayTaskIds = ReviewRequestService::getInstance()->checkDelays($taskIds);

        foreach ($data['data_list'] as $taskItem) {
            if (!empty($taskItem['plan_end_time'])) {
                $taskItem['plan_end_time'] = date("Y-m-d H:i:s", $taskItem['plan_end_time']);
            }
            if (!empty($taskItem['plan_start_time'])) {
                $taskItem['plan_start_time'] = date("Y-m-d H:i:s", $taskItem['plan_start_time']);
            }
            //如有时间计划更改 需要延期检测
            if ((!empty($taskItem['plan_start_time']) && ($taskMap[$taskItem['id']]['plan_start_time'] != strtotime($taskItem['plan_start_time']))) ||
                (!empty($taskItem['plan_end_time']) && ($taskMap[$taskItem['id']]['plan_end_time'] != strtotime($taskItem['plan_end_time'])))) {
                if (in_array($taskItem['id'], $delayTaskIds)) {
                    $errorList[] = [
                        'id' => $taskItem['id'],
                        'msg' => 'delay already apply',
                        'code' => ErrorCode::DELAY_ALREADY_APPLY
                    ];
                }
            }
            try {
                //同步外包计划时间
                TaskAssignService::getInstance()->syncOutSourceTaskPlanTime($taskItem, $taskMap[$taskItem['id']]);
            } catch (Throwable $e) {
                $errorList[] = [
                    'id' => $taskItem['id'],
                    'msg' => $e->getMessage(),
                    'code' => $e->getCode()
                ];
            }
            $flag = $this->taskModel->modifyItem($taskItem);
            if (!$flag && $this->taskModel->getErrorCode() !== \think\exception\ErrorCode::NO_DATA_HAS_BEEN_CHANGED) {
                $errorList[] = [
                    'id' => $taskItem['id'],
                    'msg' => $this->taskModel->getError(),
                    'code' => $this->taskModel->getErrorCode()
                ];
            } else {
                $successList[] = [
                    'id' => intval($taskItem['id']),
                    'plan_start_time' => empty($taskItem['plan_start_time']) ? 0 : strtotime($taskItem['plan_start_time']),
                    'plan_end_time' => empty($taskItem['plan_end_time']) ? 0 : strtotime($taskItem['plan_end_time']),
                    'manual_man_hour' => intval($taskItem['manual_man_hour'] ?? 0),
                ];
            }
        }
        return [
            'success_list' => $successList,
            'error_list' => $errorList
        ];
    }

    /**
     * 代办列表
     * @param $param
     * @param $userId
     * @param $tenantId
     * @return array|mixed|string
     */
    public function todoList($param, $userId, $tenantId)
    {
        $returnData = [
            'rows' => [],
            'total' => 0
        ];
        $filter = $param['param']['filter'] ?? [];
        $filter['task.tenant_id'] = $tenantId;
        $filter['task.entity_module_id'] = module_by_code('todo')['id'];
        $filter['task.step_type'] = ['IN', ['to_do', 'work_order_to_do', 'entity_to_do', 'standard_meeting', 'standard_review']];
        $filter = parse_filter_to_tp_where($filter, 'task');
        if (!empty($param['task_id_lock'])) {
            $lockFilter = [
                ["task.is_lock" => $param['task_id_lock']],
            ];
            if ($param['task_id_lock'] === "no") {
                $lockFilter[] = ["task.is_lock" => ['EXP', 'IS NULL']];
                $lockFilter["_logic"] = "OR";
            }
            $filter[] = $lockFilter;
        }
        $page = $param['param']['page'] ?? [1, C("database.database_max_select_rows")];
        $fields = empty($param['param']['fields']) ? 'task.id' : explode(',', $param['param']['fields']);

        if ($page[0] == 1) {
            $standardMeetingTotal = model(TaskModel::class)
                ->field($fields)
                ->join('task_member on task.id = task_member.task_id')
                ->where($filter)
                ->where(['task.step_type' => 'standard_meeting'])
                ->field("count(distinct task.id) as total")
                ->find();
            $standardReviewTotal = model(TaskModel::class)
                ->field($fields)
                ->join('task_member on task.id = task_member.task_id')
                ->where($filter)
                ->where(['task.step_type' => 'standard_review'])
                ->field("count(distinct task.id) as total")
                ->find();
            $todoTotal = model(TaskModel::class)
                ->field($fields)
                ->join('task_member on task.id = task_member.task_id')
                ->where($filter)
                ->where(['task.step_type' => ['IN', ['to_do', 'work_order_to_do', 'entity_to_do']]])
                ->field("count(distinct task.id) as total")
                ->find();
            $returnData['to_do_total'] = $todoTotal['total'] ?? 0;
            $returnData['standard_meeting_total'] = $standardMeetingTotal['total'] ?? 0;
            $returnData['standard_review_total'] = $standardReviewTotal['total'] ?? 0;
            $returnData['total'] = $returnData['to_do_total'] + $returnData['standard_meeting_total'] + $returnData['standard_review_total'];
        }
        $todoTaskList = model(TaskModel::class)
            ->field($fields)
            ->join('task_member on task.id = task_member.task_id')
            ->where($filter)
            ->page($page[0], $page[1])
            ->order($param['param']['order'] ?? 'task.id')
            ->group($param['param']['group'] ?? 'task.created')
            ->select();
        if (empty($todoTaskList)) {
            return $returnData;
        }
        $returnData['rows'] = $todoTaskList;
        return $returnData;
    }

    /**
     * 获取镜头时长
     * @param $filter
     * @return array
     */
    public function getStoryboardEstimateDuration($filter)
    {
        $projectField = [
            'id',
            'rate'
        ];
        $entityField = [
            'id',
            'name',
            'project_id',
            "JSON_UNQUOTE(JSON_EXTRACT(json,'$.storyboard_estimate_duration')) as storyboard_estimate_duration"
        ];
        $entityList = model(EntityModel::class)->field($entityField)->where($filter)->select();
        if (empty($entityList)) {
            return [];
        }
        $projectData = model(ProjectModel::class)->field($projectField)->where(['id' => $entityList[0]['project_id']])->find();
        if (empty($projectData)) {
            return [];
        }
        return array_map(function ($tmp) use ($projectData) {
            $tmp['rate'] = intval($projectData['rate']);
            $tmp['storyboard_estimate_duration'] = intval($tmp['storyboard_estimate_duration']);
            return $tmp;
        }, $entityList);
    }

    /**
     * 通过任务id,找到关联环节
     * @param $taskIds
     * @return array
     * @throws Exception
     */
    public function getTaskRelationTagStepCategory($taskIds)
    {
        if (empty($taskIds)) {
            return [];
        }
        // 上游任务关联环节
        $upstreamTaskRelationStepCategoryInfo = model(TaskRelationTagModel::class)
            ->where(['task_id' => ['IN', $taskIds], 'link_module_code' => ['IN', ['step_category', 'show_step_category']]])
            ->field(['link_id', 'task_id'])
            ->select();
        if (empty($upstreamTaskRelationStepCategoryInfo)) {
            return [];
        }
        $upstreamTaskRelationStepCategoryList = [];         // 环节详情
        $showStepCategoryList = TemplateService::getInstance()->findStepCategoryShowList();
        $showStepCategoryMap = array_column($showStepCategoryList, null, 'id');
        foreach ($upstreamTaskRelationStepCategoryInfo as $upstreamTaskRelationStepCategoryItem) {
            if (!empty($showStepCategoryMap[$upstreamTaskRelationStepCategoryItem['link_id']])) {
                $upstreamTaskRelationStepCategoryList[] = [
                    'task_id' => $upstreamTaskRelationStepCategoryItem['task_id'],
                    'step_category_id' => $showStepCategoryMap[$upstreamTaskRelationStepCategoryItem['link_id']]
                ];
            }
        }
        return $upstreamTaskRelationStepCategoryList;
    }
}
